/* Copyright (c) 2017, NVIDIA CORPORATION.  All rights reserved.
*
* NVIDIA CORPORATION and its licensors retain all intellectual property
* and proprietary rights in and to this software, related documentation
* and any modifications thereto.  Any use, reproduction, disclosure or
* distribution of this software and related documentation without an express
* license agreement from NVIDIA CORPORATION is strictly prohibited.
*/
#include "NVIDIAGfeSDKPrivatePCH.h"
#include "GfeSDKHighlights.h"
#include "Platform.h"

#define VALIDATE_MODULE_HIGHLIGHTS()                                           \
    if (!GFEHandle && Callback)                                    \
    {                                                               \
        Callback(GfeSDK::NVGSDK_ERR_MODULE_NOT_LOADED, Context);    \
        return;                                                     \
    }

#define VALIDATE_MODULE_HIGHLIGHTS_SPECIALIZED()                                       \
    if (!GFEHandle && Callback)                                            \
    {                                                                       \
        Callback(GfeSDK::NVGSDK_ERR_MODULE_NOT_LOADED, nullptr, Context);   \
        return;                                                             \
    }

struct LocaleStringPair
{
	std::string localeCode;
	std::string localizedString;
};

struct LocaleHighlightsList
{
	std::vector<LocaleStringPair> Strings;
	std::vector<GfeSDK::NVGSDK_LocalizedPair> Pairs;
};

FGfeSDKHighlights::FGfeSDKHighlights(GfeSDK::NVGSDK_HANDLE* Handle) :
    GFEHandle(Handle)
{}

FGfeSDKHighlights::~FGfeSDKHighlights()
{}

void FGfeSDKHighlights::ConfigureAsync(FGfeSDKHighlightConfigParams const& Params, GfeSDK::NVGSDK_EmptyCallback Callback, void* Context)
{	
    VALIDATE_MODULE_HIGHLIGHTS();

	std::string UTF8DefaultLocale = TCHAR_TO_UTF8(*Params.DefaultLocale);
	GfeSDK::NVGSDK_HighlightConfigParams cParams;
	memset(&cParams, 0, sizeof(cParams));
	cParams.defaultLocale = UTF8DefaultLocale.c_str();

	// Temporary storages for structure elements required by ConfigureAsync function
	std::vector<GfeSDK::NVGSDK_Highlight> Highlights(Params.HighlightDefinitions.Num());
	// TCHAR_TO_UTF8 shares internal buffer, so its results need to be assigned by copy, not pointer.
	std::vector <std::string> id(Params.HighlightDefinitions.Num()); // Workaround for usage of TCHAR_TO_UTF8.
	std::vector<LocaleHighlightsList> LocaleStringPairs(Params.HighlightDefinitions.Num());

    for (int32 i=0; i<Params.HighlightDefinitions.Num(); i++)
    {
		GfeSDK::NVGSDK_Highlight def;
        
		const FGfeSDKHighlightDefinition& ueDef(Params.HighlightDefinitions[i]);
		id[i] = TCHAR_TO_UTF8(*ueDef.Id);
		def.id = id[i].c_str();
		def.highlightTags = TranslateBitfieldEnum(kHighlightTypeUnrealToSdk, ueDef.HighlightTags);
		def.significance = TranslateBitfieldEnum(kHighlightSignificanceUnrealToSdk, ueDef.Significance);
		def.userInterest = ueDef.UserDefaultInterest;

		LocaleStringPairs[i].Pairs.resize(ueDef.NameTranslationTable.Num());
		LocaleStringPairs[i].Strings.resize(ueDef.NameTranslationTable.Num());

		int32 s = 0;
		for (auto& Entry : ueDef.NameTranslationTable)
        {			
			LocaleStringPairs[i].Strings[s].localeCode = TCHAR_TO_UTF8(*Entry.Key);
			LocaleStringPairs[i].Strings[s].localizedString = TCHAR_TO_UTF8(*Entry.Value);

			LocaleStringPairs[i].Pairs[s].localeCode = LocaleStringPairs[i].Strings[s].localeCode.c_str();
			LocaleStringPairs[i].Pairs[s].localizedString = LocaleStringPairs[i].Strings[s].localizedString.c_str();
			s++;
        }
		
		def.nameTable = LocaleStringPairs[i].Pairs.data();
		def.nameTableSize = LocaleStringPairs[i].Pairs.size();

		Highlights[i] = def;
    }
	cParams.highlightDefinitionTable = Highlights.data();
	cParams.highlightTableSize = Highlights.size();
	GfeSDK::NVGSDK_Highlights_ConfigureAsync(GFEHandle, &cParams, Callback, Context);
}

void FGfeSDKHighlights::GetUserSettingsAsync(GfeSDK::NVGSDK_Highlights_GetUserSettingsCallback Callback, void* Context) const
{
    VALIDATE_MODULE_HIGHLIGHTS_SPECIALIZED();
	GfeSDK::NVGSDK_Highlights_GetUserSettingsAsync(GFEHandle, Callback, Context);
}

void FGfeSDKHighlights::OpenGroupAsync(FGfeSDKHighlightOpenGroupParams const& Params, GfeSDK::NVGSDK_EmptyCallback Callback, void* Context)
{
    VALIDATE_MODULE_HIGHLIGHTS();

	LocaleHighlightsList LocaleHighlights;
	LocaleHighlights.Pairs.resize(Params.GroupDescriptionTranslationTable.Num());
	LocaleHighlights.Strings.resize(Params.GroupDescriptionTranslationTable.Num());

	GfeSDK::NVGSDK_HighlightOpenGroupParams params;
	memset(&params, 0, sizeof(params));

	const std::string groupId(TCHAR_TO_UTF8(*Params.GroupId));
	params.groupId = groupId.c_str();

	int32 s = 0;
    for (auto& Entry : Params.GroupDescriptionTranslationTable)
    {
		LocaleHighlights.Strings[s].localeCode = TCHAR_TO_UTF8(*Entry.Key);
		LocaleHighlights.Strings[s].localizedString = TCHAR_TO_UTF8(*Entry.Value);
		LocaleHighlights.Pairs[s].localeCode = LocaleHighlights.Strings[s].localeCode.c_str();
		LocaleHighlights.Pairs[s].localizedString = LocaleHighlights.Strings[s].localizedString.c_str();
		s++;
    }

	params.groupDescriptionTable = LocaleHighlights.Pairs.data();
	params.groupDescriptionTableSize = LocaleHighlights.Pairs.size();

	GfeSDK::NVGSDK_Highlights_OpenGroupAsync(GFEHandle, &params, Callback, Context);
}

void FGfeSDKHighlights::CloseGroupAsync(FGfeSDKHighlightCloseGroupParams const& Params, GfeSDK::NVGSDK_EmptyCallback Callback, void* Context)
{
    VALIDATE_MODULE_HIGHLIGHTS();

	GfeSDK::NVGSDK_HighlightCloseGroupParams params;
	memset(&params, 0, sizeof(params));

	std::string ID = TCHAR_TO_UTF8(*Params.GroupId);
	params.groupId = ID.c_str();
	params.destroyHighlights = Params.DestroyHighlights;

	GfeSDK::NVGSDK_Highlights_CloseGroupAsync(GFEHandle, &params, Callback, Context);
}

void FGfeSDKHighlights::SetScreenshotHighlightAsync(FGfeSDKHighlightScreenshotParams const& Params, GfeSDK::NVGSDK_EmptyCallback Callback, void* Context)
{
    VALIDATE_MODULE_HIGHLIGHTS();
	std::string gID = TCHAR_TO_UTF8(*Params.GroupId);
	std::string hID = TCHAR_TO_UTF8(*Params.HighlightId);

	GfeSDK::NVGSDK_ScreenshotHighlightParams params;
	memset(&params, 0, sizeof(params));

    params.groupId = gID.c_str();
    params.highlightId = hID.c_str();

	GfeSDK::NVGSDK_Highlights_SetScreenshotHighlightAsync(GFEHandle, &params, Callback, Context);
}

void FGfeSDKHighlights::SetVideoHighlightAsync(FGfeSDKHighlightVideoParams const& Params, GfeSDK::NVGSDK_EmptyCallback Callback, void* Context)
{
    VALIDATE_MODULE_HIGHLIGHTS();
	GfeSDK::NVGSDK_VideoHighlightParams cParams;
	memset(&cParams, 0, sizeof(cParams));

	std::string gID = TCHAR_TO_UTF8(*Params.GroupId);
	std::string hID = TCHAR_TO_UTF8(*Params.HighlightId);

	cParams.groupId = gID.c_str();
	cParams.highlightId = hID.c_str();
	cParams.endDelta = Params.EndDelta;
	cParams.startDelta = Params.StartDelta;

	GfeSDK::NVGSDK_Highlights_SetVideoHighlightAsync(GFEHandle, &cParams, Callback, Context);
}

void FGfeSDKHighlights::OpenSummaryAsync(FGfeSDKHighlightSummaryParams const& Params, GfeSDK::NVGSDK_EmptyCallback Callback, void* Context)
{
    VALIDATE_MODULE_HIGHLIGHTS();
	GfeSDK::NVGSDK_SummaryParams cParams;
	memset(&cParams, 0, sizeof(cParams));

	std::vector<GfeSDK::NVGSDK_GroupView> cGroupViews(Params.GroupViews.Num());
	std::vector<std::string> StringCache(Params.GroupViews.Num());

	for (size_t i = 0; i < cGroupViews.size(); i++)
	{
		StringCache[i] = TCHAR_TO_UTF8(*Params.GroupViews[i].GroupId);
		cGroupViews[i].groupId = StringCache[i].c_str();
		cGroupViews[i].tagsFilter = TranslateBitfieldEnum(kHighlightTypeUnrealToSdk, Params.GroupViews[i].TagsFilter);
		cGroupViews[i].significanceFilter = TranslateBitfieldEnum(kHighlightSignificanceUnrealToSdk, Params.GroupViews[i].SignificanceFilter);
	}
	cParams.groupSummaryTable = cGroupViews.data();
	cParams.groupSummaryTableSize = cGroupViews.size();

	GfeSDK::NVGSDK_Highlights_OpenSummaryAsync(GFEHandle, &cParams, Callback, Context);
}

void FGfeSDKHighlights::GetNumberOfHighlightsAsync(FGfeSDKHighlightGroupView const& GroupView, GfeSDK::NVGSDK_Highlights_GetNumberOfHighlightsCallback Callback, void* Context)
{
    VALIDATE_MODULE_HIGHLIGHTS_SPECIALIZED();
    GfeSDK::NVGSDK_GroupView groupView;
	memset(&groupView, 0, sizeof(groupView));

	std::string gID = TCHAR_TO_UTF8(*GroupView.GroupId);
	groupView.groupId = gID.c_str();
    groupView.tagsFilter = TranslateBitfieldEnum(kHighlightTypeUnrealToSdk, GroupView.TagsFilter);
    groupView.significanceFilter = TranslateBitfieldEnum(kHighlightSignificanceUnrealToSdk, GroupView.SignificanceFilter);

	GfeSDK::NVGSDK_Highlights_GetNumberOfHighlightsAsync(GFEHandle, &groupView, Callback, Context);
}

struct SNumHighlightsInfo
{
	volatile bool  Ready;
	int32 NumberOfHighlights;

	SNumHighlightsInfo():
		Ready(false),
		NumberOfHighlights(0)
	{}
};

void __stdcall OnGetNumberOfHighlightsCallbackBlocking(GfeSDK::NVGSDK_RetCode rt, GfeSDK::NVGSDK_Highlights_NumberOfHighlights const* NumberOfHighlights, void* context)
{
	SNumHighlightsInfo* NCB = static_cast<SNumHighlightsInfo*>(context);
	if (NCB)
	{
		NCB->NumberOfHighlights = NumberOfHighlights->numberOfHighlights;
		NCB->Ready = true;
	}
}

int32 FGfeSDKHighlights::GetNumberOfHighlights(FGfeSDKHighlightGroupView const& GroupView)
{
	SNumHighlightsInfo NCB;	
	GetNumberOfHighlightsAsync(GroupView, &OnGetNumberOfHighlightsCallbackBlocking, &NCB);
	int32 IterationCounter = 0;
	// Polling cycle (not sure if this is correct for UE4, if we need a blocking function)
	// Wait for 10 seconds and return 0 if time is up
	while (!NCB.Ready && (IterationCounter < 1000))
	{
		Poll();
		FPlatformProcess::Sleep(0.001f);
		IterationCounter++;
	}
	return NCB.NumberOfHighlights;
}

void FGfeSDKHighlights::Poll()
{
	if (GFEHandle)
		GfeSDK::NVGSDK_Poll(GFEHandle);
}